/*
 * Author: hujingfei914@msn.com
 * Date:   2010-10-14
 * Description: A custom picture control supporting rendering using OpenGL
 */
#include "OpenGLControl.h"
#include "3DSAircraft.h"
#include "Texture.h"
#include "3DSLoader.h"

COpenGLControl::COpenGLControl(void)
{
	m_fPosX = 0.0f;
	m_fPosY = 0.0f;
	m_fRotX = 0.0f;
	m_fRotY = 0.0f;	
	m_fZoom = 400.0f;

	//aircraft = new CAircraft("models\\bi_plane.wrl", -20.0f, -120.0f, 0.0f, 0.0f, 180.0f, 0.0f);
	spaceship = new C3DSAircraft(0.0f, 0.0f, 0.0f, -90.0f, 0.0f, 180.0f);
	spaceship->setNumberOfObjects(
		C3DSLoader::LoadMultiple3DSObjects(spaceship->getPointerOfObjects(), "models\\spaceship.3DS"));
}

COpenGLControl::~COpenGLControl(void)
{
	//delete aircraft;
	delete spaceship;
}

void COpenGLControl::oglCreate(CRect rect, CWnd *parent)
{
	CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
		NULL, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL);
	CreateEx(0, className, _T("OpenGL"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
		, rect, parent, 0);

	// Set initial variables' values
	m_oldWindow = rect;
	m_originalRect = rect;

	hWnd = parent;
}
void COpenGLControl::oglInitialize(void)
{
	// Initial setup:
	static PIXELFORMATDESCRIPTOR pfd = 
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,		// BIT DEPTH
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		16,     // Z-BUFFER DEPTH
		0, 0, 0, 0, 0, 0, 0,
	};

	// Get device context only once.
	hdc = GetDC()->m_hDC;

	// Pixel format
	m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
	SetPixelFormat(hdc, m_nPixelFormat, &pfd);

	// Create the openGL Rendering context
	hrc = wglCreateContext(hdc);
	wglMakeCurrent(hdc, hrc);

	// Basic setup:

	// Set color to use when clearing the background
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClearDepth(1.0f);

	// Set the shade model
	glShadeModel(GL_SMOOTH);

	// Turn on backface fulling
	//glFrontFace(GL_CCW);
	//glCullFace(GL_BACK);

	// Turn on depth testing
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	// Set the illumination and material properties
	//oglInitializeIlluminationAndMaterial();

	// Set the texture
	glEnable(GL_TEXTURE_2D);
	GLuint texId;
	loadTexture("models\\spaceshiptexture.bmp", texId);
	spaceship->setTextureId(0, texId);

	// Send draw request
	OnDraw(NULL);

}
BEGIN_MESSAGE_MAP(COpenGLControl, CWnd)
	ON_WM_PAINT()
	ON_WM_CREATE()
	ON_WM_TIMER()
	ON_WM_SIZE()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()

void COpenGLControl::OnPaint()
{
	//CPaintDC dc(this); // device context for painting
	// TODO: Add your message handler code here
	// Do not call CWnd::OnPaint() for painting messages
	ValidateRect(NULL);
}

int COpenGLControl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  Add your specialized creation code here
	oglInitialize();

	return 0;
}

void COpenGLControl::OnDraw(CDC *pDC)
{
	// TODO: Camera controls.
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0f, 0.0f, -m_fZoom);
	glTranslatef(m_fPosX, m_fPosY, 0.0f);
	glRotatef(m_fRotX, 1.0f, 0.0f, 0.0f);
	glRotatef(m_fRotY, 0.0f, 1.0f, 0.0f);
}

void COpenGLControl::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	switch (nIDEvent) {
		case 10:
		{
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			// Draw OpenGL Scene
			//oglDrawScene();
			//aircraft->draw();
			spaceship->draw();

			// Swap buffers
			SwapBuffers(hdc);

			break;
		}

		default:
			break;
	}

	CWnd::OnTimer(nIDEvent);
}

void COpenGLControl::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);

	if (0 >= cx || 0 >= cy || nType == SIZE_MINIMIZED) return;

	// Map the OpenGL coordinates.
	glViewport(0, 0, cx, cy);

	// Projection view
	glMatrixMode(GL_PROJECTION);

	glLoadIdentity();

	// Set our current view perspective
	gluPerspective(35.0f, (float)cx / (float)cy, 0.01f, 2000.0f);

	// Model View
	glMatrixMode(GL_MODELVIEW);
}

void COpenGLControl::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default

	//if (aircraft) 
	//{
	//	aircraft->updatePositionAndOrientation(nFlags, point);
	//}
	if (spaceship) 
	{
		spaceship->updatePositionAndOrientation(nFlags, point);
	}
	OnDraw(NULL);
	CWnd::OnMouseMove(nFlags, point);
}

void COpenGLControl::oglInitializeIlluminationAndMaterial(void)
{
	GLfloat mat_ambient[]= { 0.2f, 0.2f, 0.2f, 1.0f };
	GLfloat mat_diffuse[]= { 0.4f, 1.0f, 0.8f, 1.0f };
	GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };	
	GLfloat mat_emission[] = { 0.5f, 0.5f, 0.5f, 1.0f };
	GLfloat mat_shininess[] = {5.0f};
	GLfloat mat_no[] = {0.0f, 0.0f, 0.0f, 1.0f};
	
	GLfloat light0_position[] = {0.0f, 0.0f, 1.0f, 0.0f};
	GLfloat light0_ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f };
	GLfloat light0_diffuse[]= { 0.8f, 0.8f, 0.8f, 1.0f };
	GLfloat light0_specular[]= { 0.8f, 0.8f, 0.8f, 1.0f };
	
	GLfloat lmodel_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
	
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_no);

	glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);		
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light0_position);

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
}


void COpenGLControl::updateAircraft(const FlyState &flyState)
{
	spaceship->updatePositionAndOrientation(flyState);
}